/******************************************************************************\
 *                                                                            *
 *                          Craftmesh Viewer                                  *
 *                   Flavio Bertini fl.bertini@gmail.com                      *
 *                                                                            *
 *                                                                            *
\******************************************************************************/


#include "Craftmesh_viewer.h"


using namespace std;


// Create the window and the camera of the project
Vew_GlutWindow _window;
Vew_GlutCamera _camera;

// External resources
extern Controller                 _controller;
extern Con_CraftmeshListener      _listener;
extern vector<Mod_SubdivMesh>     _mesh;
extern vector<Con_HandController> _hands;
extern Vector _axeXupdate;
extern Vector _axeYupdate;
extern Vector _axeZupdate;
extern float  _angleXZ;

// local resources
Vew_HandTexture handTexture;
GLfloat _topLeftSelecPlane[3],
        _topRightSelecPlane[3],
        _bottomRightSelecPlane[3],
        _bottomLeftSelecPlane[3];

// boolean operations structure
vector<int> m;
vector<Mod_SubdivMesh*> result;
Mod_SubdivMesh tempMesh;

// Window stuff
// Constructor
Vew_GlutWindow::Vew_GlutWindow()
{
  // set window stuff
  title        = "*** CraftMesh ***";
  width        = 300;
  height       = 300;
  anaglyphType = red_blue;
  enaxes       = true;
  engrid       = true;
  enSphere     = false;
  enSolid      = false;
  mode         = object;
  palmPlan     = false;
  fingPos      = false;
  helpMenu     = false;
  encyli       = false;
  vmode        = GL_VMONO;

  selecPlane   = false;
  selecSphere  = false;

  // set application's camera mode menu
  menuCamera.push_back("HELP (camera mode):");
  menuCamera.push_back("A   - switch anaglyph type");
  menuCamera.push_back("a   - enable/disable system's axes");
  menuCamera.push_back("e/E - anaglyph eyes separation");
  menuCamera.push_back("g   - enable/disable background grid");
  menuCamera.push_back("h   - enable/disable this help");
  menuCamera.push_back("p/P - convergence plane distance");
  menuCamera.push_back("R   - reset camera position");
  menuCamera.push_back("V   - change the vision mode");
  menuCamera.push_back("v   - enable/disable solid mesh");
  menuCamera.push_back("z/Z - camera zoom in/out");
  menuCamera.push_back("1   - skip to camera");
  menuCamera.push_back("2   - skip to object");
  menuCamera.push_back("3   - skip to edit mode");
  menuCamera.push_back("4   - skip to cut mode");
  menuCamera.push_back("5   - skip to boolean mode");
  menuCamera.push_back("6   - skip to sculpture mode");
  menuCamera.push_back("esc - back to object mode");
  // set application's object mode menu
  menuObject.push_back("HELP (object mode):");
  menuObject.push_back("a   - enable/disable system's axes");
  menuObject.push_back("c   - capture a mesh");
  menuObject.push_back("f   - enable/disable fingers reference");
  menuObject.push_back("g   - enable/disable background grid");
  menuObject.push_back("h   - enable/disable this help");
  menuObject.push_back("m   - enable/disable mesh reference");
  menuObject.push_back("S   - save obj mesh");
  menuObject.push_back("V   - change the vision mode");
  menuObject.push_back("v   - enable/disable solid mesh");
  menuObject.push_back("z/Z - camera zoom in/out");
  menuObject.push_back("1   - skip to camera");
  menuObject.push_back("2   - skip to object");
  menuObject.push_back("3   - skip to edit mode");
  menuObject.push_back("4   - skip to cut mode");
  menuObject.push_back("5   - skip to boolean mode");
  menuObject.push_back("6   - skip to sculpture mode");
  menuObject.push_back("esc - quit");
  // set application's edit mode menu
  menuEdit.push_back("HELP (edit mode):");
  menuEdit.push_back("a   - enable/disable system's axes");
  menuEdit.push_back("c   - capture a mesh");
  menuEdit.push_back("d   - deselect all vertices");
  menuEdit.push_back("f   - enable/disable fingers reference");
  menuEdit.push_back("g   - enable/disable background grid");
  menuEdit.push_back("h   - enable/disable this help");
  menuEdit.push_back("i   - invert selection");
  menuEdit.push_back("m   - enable/disable mesh reference");
  menuEdit.push_back("p   - select vertex by plane on/off");
  menuEdit.push_back("s   - select vertex by sphere on/off");
  menuEdit.push_back("V   - change the vision mode");
  menuEdit.push_back("v   - enable/disable solid mesh");
  menuEdit.push_back(">   - select vertex");
  menuEdit.push_back("<   - deselect vertex");
  menuEdit.push_back("z/Z - camera zoom in/out");
  menuEdit.push_back("1   - skip to camera");
  menuEdit.push_back("2   - skip to object");
  menuEdit.push_back("3   - skip to edit mode");
  menuEdit.push_back("4   - skip to cut mode");
  menuEdit.push_back("5   - skip to boolean mode");
  menuEdit.push_back("6   - skip to sculpture mode");
  menuEdit.push_back("esc - back to object mode");
  // set application's cut mode menu
  menuCut.push_back("HELP (cut mode):");
  menuCut.push_back("a   - enable/disable system's axes");
  menuCut.push_back("f   - enable/disable fingers reference");
  menuCut.push_back("g   - enable/disable background grid");
  menuCut.push_back("h   - enable/disable this help");
  menuCut.push_back("m   - enable/disable mesh reference");
  menuCut.push_back("V   - change the vision mode");
  menuCut.push_back("v   - enable/disable solid mesh");
  menuCut.push_back("z/Z - camera zoom in/out");
  menuCut.push_back("1   - skip to camera");
  menuCut.push_back("2   - skip to object");
  menuCut.push_back("3   - skip to edit mode");
  menuCut.push_back("4   - skip to cut mode");
  menuCut.push_back("5   - skip to boolean mode");
  menuCut.push_back("6   - skip to sculpture mode");
  menuCut.push_back("esc - back to object mode");
  // set application's boolean mode menu
  menuBoolean.push_back("HELP (boolean mode):");
  menuBoolean.push_back("a   - enable/disable system's axes");
  menuBoolean.push_back("b   - selecting mesh for boolean operation");
  menuBoolean.push_back("c   - capture a mesh");
  menuBoolean.push_back("f   - enable/disable fingers reference");
  menuBoolean.push_back("g   - enable/disable background grid");
  menuBoolean.push_back("h   - enable/disable this help");
  menuBoolean.push_back("m   - enable/disable mesh reference");
  menuBoolean.push_back("R   - run boolean operation");
  menuBoolean.push_back("V   - change the vision mode");
  menuBoolean.push_back("v   - enable/disable solid mesh");
  menuBoolean.push_back("z/Z - camera zoom in/out");
  menuBoolean.push_back("1   - skip to camera");
  menuBoolean.push_back("2   - skip to object");
  menuBoolean.push_back("3   - skip to edit mode");
  menuBoolean.push_back("4   - skip to cut mode");
  menuBoolean.push_back("5   - skip to boolean mode");
  menuBoolean.push_back("6   - skip to sculpture mode");
  menuBoolean.push_back("esc - back to object mode");
  // set application's sculpture mode menu
  menuSculpture.push_back("HELP (sculpture mode):");
  menuSculpture.push_back("a   - enable/disable system's axes");
  menuSculpture.push_back("f   - enable/disable fingers reference");
  menuSculpture.push_back("g   - enable/disable background grid");
  menuSculpture.push_back("h   - enable/disable this help");
  menuSculpture.push_back("m   - enable/disable mesh reference");
  menuSculpture.push_back("V   - change the vision mode");
  menuSculpture.push_back("v   - enable/disable solid mesh");
  menuSculpture.push_back("z/Z - camera zoom in/out");
  menuSculpture.push_back("1   - skip to camera");
  menuSculpture.push_back("2   - skip to object");
  menuSculpture.push_back("3   - skip to edit mode");
  menuSculpture.push_back("4   - skip to cut mode");
  menuSculpture.push_back("5   - skip to boolean mode");
  menuSculpture.push_back("6   - skip to sculpture mode");
  menuSculpture.push_back("esc - back to object mode");

  return;
}

// Destructor
Vew_GlutWindow::~Vew_GlutWindow() {}

// initialize the window
void Vew_GlutWindow::vew_initWindow(int argc, char** argv)
{

  // initialize GLUT environment
  glutInit(&argc, argv);                                // GLUT initialization
  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE |          // display Mode
                      GLUT_DEPTH | GLUT_MULTISAMPLE);
  glutInitWindowSize(width, height);                    // window size
  glutInitWindowPosition(0, 0);                         // window position
  Id = glutCreateWindow(title);                         // create Window
  glutFullScreen();
  glutDisplayFunc(vew_display);
  glutIdleFunc(vew_display);
  glutKeyboardFunc(vew_keyboard);

  // initialize some environment stuff
  glClearColor(0.0, 0.0, 0.0, 1.0);                     // specify clear values for the color buffers
  glClearDepth(1.0f);                                   // specify the clear value for the depth buffer
  glEnable(GL_MULTISAMPLE);                             // multisampling is not the most sophisticated anti-aliasing
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);    // specify implementation-specific hints

  // set light
  GLfloat fullAmbient[4] = {1.0, 1.0, 1.0, 1.0};
  GLfloat position[4] = {(GLfloat)_camera.lookAt[0], (GLfloat)_camera.lookAt[1], (GLfloat)_camera.lookAt[2], 0.0};
  GLfloat lb_ambient[4]  = {0.0, 0.0, 0.3, 1.0};  // blu
  GLfloat lb_diffuse[4]  = {0.0, 0.0, 1.0, 1.0};
  GLfloat lb_specular[4] = {0.0, 0.0, 0.1, 1.0};
  GLfloat lc_ambient[4]  = {0.0, 0.3, 0.3, 1.0};  // cyan
  GLfloat lc_diffuse[4]  = {0.0, 1.0, 1.0, 1.0};
  GLfloat lc_specular[4] = {0.0, 0.1, 0.1, 1.0};
  GLfloat lr_ambient[4]  = {0.3, 0.0, 0.0, 1.0};  // red
  GLfloat lr_diffuse[4]  = {1.0, 0.0, 0.0, 1.0};
  GLfloat lr_specular[4] = {0.1, 0.0, 0.0, 1.0};
  GLfloat ambient[4]     = {0.3, 0.3, 0.3, 1.0};
  GLfloat diffuse[4]     = {1.0, 1.0, 1.0, 1.0};
  GLfloat specular[4]    = {0.1, 0.1, 0.1, 1.0};
  glDisable(GL_LIGHT0);                                       // turn off all the lights
  glDisable(GL_LIGHT1);
  glDisable(GL_LIGHT2);
  glDisable(GL_LIGHT3);
  glDisable(GL_LIGHT4);
  glDisable(GL_LIGHT5);
  glDisable(GL_LIGHT6);
  glDisable(GL_LIGHT7);
  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);        // more realistic results less performance
  glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);           // only front faces
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fullAmbient);        // set and turn on the light zero
  glLightfv(GL_LIGHT0, GL_POSITION, position);
  glLightfv(GL_LIGHT0, GL_AMBIENT,  lb_ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE,  lb_diffuse);
  glLightfv(GL_LIGHT0, GL_SPECULAR, lb_specular);
  glLightfv(GL_LIGHT1, GL_POSITION, position);
  glLightfv(GL_LIGHT1, GL_AMBIENT,  lr_ambient);
  glLightfv(GL_LIGHT1, GL_DIFFUSE,  lr_diffuse);
  glLightfv(GL_LIGHT1, GL_SPECULAR, lr_specular);
  glLightfv(GL_LIGHT2, GL_POSITION, position);
  glLightfv(GL_LIGHT2, GL_AMBIENT,  ambient);
  glLightfv(GL_LIGHT2, GL_DIFFUSE,  diffuse);
  glLightfv(GL_LIGHT2, GL_SPECULAR, specular);
  glLightfv(GL_LIGHT3, GL_POSITION, position);
  glLightfv(GL_LIGHT3, GL_AMBIENT,  lc_ambient);
  glLightfv(GL_LIGHT3, GL_DIFFUSE,  lc_diffuse);
  glLightfv(GL_LIGHT3, GL_SPECULAR, lc_specular);
  glEnable(GL_LIGHT0);
  glEnable(GL_LIGHT1);
  glEnable(GL_LIGHT2);
  glEnable(GL_LIGHT3);
  glShadeModel(GL_SMOOTH);                                    // sort out the shading algorithm
  glEnable(GL_LIGHTING);                                      // enable lighting

  // loading textures
  handTexture.handImage.resize(6);
  handTexture.texHandImage.resize(6);
  // fist
  handTexture.handImage[0] = (GLubyte *)vew_rgbLoader("texture/hand0.rgba",
                             &(handTexture.wHandImage), &(handTexture.hHandImage),
                             &(handTexture.cHandImage), GL_TRUE);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glGenTextures(1, &(handTexture.texHandImage[0]));
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  // one finger
  handTexture.handImage[1] = (GLubyte *)vew_rgbLoader("texture/hand1.rgba",
                             &(handTexture.wHandImage), &(handTexture.hHandImage),
                             &(handTexture.cHandImage), GL_TRUE);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glGenTextures(1, &(handTexture.texHandImage[1]));
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  // two fingers
  handTexture.handImage[2] = (GLubyte *)vew_rgbLoader("texture/hand2.rgba",
                             &(handTexture.wHandImage), &(handTexture.hHandImage),
                             &(handTexture.cHandImage), GL_TRUE);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glGenTextures(1, &(handTexture.texHandImage[2]));
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  // three fingers
  handTexture.handImage[3] = (GLubyte *)vew_rgbLoader("texture/hand3.rgba",
                             &(handTexture.wHandImage), &(handTexture.hHandImage),
                             &(handTexture.cHandImage), GL_TRUE);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glGenTextures(1, &(handTexture.texHandImage[3]));
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  // four fingers
  handTexture.handImage[4] = (GLubyte *)vew_rgbLoader("texture/hand4.rgba",
                             &(handTexture.wHandImage), &(handTexture.hHandImage),
                             &(handTexture.cHandImage), GL_TRUE);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glGenTextures(1, &(handTexture.texHandImage[4]));
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  // five fingers
  handTexture.handImage[5] = (GLubyte *)vew_rgbLoader("texture/hand5.rgba",
                             &(handTexture.wHandImage), &(handTexture.hHandImage),
                             &(handTexture.cHandImage), GL_TRUE);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glGenTextures(1, &(handTexture.texHandImage[5]));
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

  glutMainLoop();                                             // run GLUT mainloop
  return;
}

// draw text in 2D
void Vew_GlutWindow::vew_drawString2D(string text, int posx, int posy, const GLfloat* color)
{
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glOrtho(0, width, 0, height, -1.0f, 1.0f);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glPushAttrib(GL_DEPTH_TEST);
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_LIGHTING);
  glColor3fv(color);
  glRasterPos2i(posx, posy);
  for (string::iterator strIter=text.begin(); strIter!=text.end(); strIter++)
    glutBitmapCharacter(GLUT_BITMAP_8_BY_13, *strIter);
  glEnable(GL_LIGHTING);
  glEnable(GL_DEPTH_TEST);
  glPopAttrib();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
  glPopMatrix();
  return;
}

// from number to string
template <typename T> string ntos(const T& t) { ostringstream os; os<<t; return os.str(); }

// draw application's menu
void Vew_GlutWindow::vew_drawMenu(int visionMode)
{
  int i=20;
  vector<string> menu;
  // select menu item
  switch (_window.mode)
  {
    case camera:
    {
      string eyes  = "e/E - anaglyph eyes separation [6] ";
      string cpla  = "p/P - convergence plane distance [-90] ";
      menuCamera[3] = eyes.append(ntos(_camera.eyeDelta));
      menuCamera[6] = cpla.append(ntos(_camera.folDista));
      menu = menuCamera;
      break;
    }
    case object:
    {
      menu = menuObject;
      break;
    }
    case edit:
    {
      menu = menuEdit;
      break;
    }
    case cut:
    {
      menu = menuCut;
      break;
    }
    case boolean:
    {
      menu = menuBoolean;
      break;
    }
    case sculpture:
    {
      menu = menuSculpture;
      break;
    }
  }

  // draw menu
  for (vector<string>::iterator vec_iter=menu.begin(); vec_iter!=menu.end(); vec_iter++)
  {
    if (visionMode == GL_VMONO || visionMode == GL_VMULTI)
    {
      glViewport(0, 0, _window.width, _window.height);
      vew_drawString2D(*vec_iter, 8, height-i, GL_YELLOW_COLOR);
    }
    else vew_drawString2D(*vec_iter, 8, height-i, GL_GREY_COLOR);
    i += 20;
  }

  return;
}

// draw the background grids
void Vew_GlutWindow::vew_grid(int visionMode)
{
  GLdouble unit = 10.0;      // one line every 10mm
  glDisable(GL_LIGHTING);
  GLdouble tmp = -150.0;
  for (int i=0; i<=30; i++)
  {
    if (visionMode == GL_VMONO || visionMode == GL_VMULTI)
    {
      if (i%5 == 0)
      {
        glColor3fv(GL_SOFTRED_COLOR);
        glLineWidth(2);
      }
      else
      {
        glColor3fv(GL_SOFTGREY_COLOR);
        glLineWidth(0.5);
      }
    }
    else
    {
      glColor3fv(GL_GREY_COLOR);
      if (i%5 == 0)
        glLineWidth(2);
      else
        glLineWidth(0.5);
    }
    glBegin(GL_LINES);
      // xz-plane
      glVertex3f(tmp, 0.0, -150.0);
      glVertex3f(tmp, 0.0, 150.0);
      // xz-plane
      glVertex3f(-150.0, 0.0, tmp);
      glVertex3f(150.0, 0.0, tmp);
      // xy-axis
      glVertex3f(tmp, 0.0, -150.0);
      glVertex3f(tmp, 350.0, -150.0);
    glEnd();
    tmp += unit;
  }
  tmp = 0.0;
  for (int i=0; i<=35; i++)
  {
    if (visionMode == GL_VMONO || visionMode == GL_VMULTI)
    {
      if (i%5 == 0)
      {
        glColor3fv(GL_SOFTRED_COLOR);
        glLineWidth(2);
      }
      else
      {
        glColor3fv(GL_SOFTGREY_COLOR);
        glLineWidth(0.5);
      }
    }
    else
    {
      glColor3fv(GL_GREY_COLOR);
      if (i%5 == 0)
        glLineWidth(2);
      else
        glLineWidth(0.5);
    }

    // xy-axis
    glBegin(GL_LINES);
      glVertex3f(-150.0, tmp, -150.0);
      glVertex3f(150.0, tmp, -150.0);
    glEnd();
    tmp += unit;
  }
  glEnable(GL_LIGHTING);
  return;
}

// draw system's axes
void Vew_GlutWindow::vew_axes(int visionMode)
{
  glDisable(GL_LIGHTING);
  glLineWidth(2.0);
  // x-axis
  if (visionMode == GL_VMONO || visionMode == GL_VMULTI) glColor3fv(GL_RED_COLOR);
  else glColor3fv(GL_GREY_COLOR);
  glBegin(GL_LINES);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(10.0, 0.0, 0.0);
  glEnd();
  // y-axis
  if (visionMode == GL_VMONO || visionMode == GL_VMULTI) glColor3fv(GL_GREEN_COLOR);
  else glColor3fv(GL_GREY_COLOR);
  glBegin(GL_LINES);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(0.0, 10.0, 0.0);
  glEnd();
  // z-axis
  if (visionMode == GL_VMONO || visionMode == GL_VMULTI) glColor3fv(GL_BLU_COLOR);
  else glColor3fv(GL_GREY_COLOR);
  glBegin(GL_LINES);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(0.0, 0.0, 10.0);
  glEnd();

  glEnable(GL_LIGHTING);
  return;
}

// draw tip finger position
void Vew_GlutWindow::vew_drawFingersSphere(int visionMode)
{
  glDisable(GL_LIGHTING);
  for (unsigned int i=0; i<_hands.size(); i++)
  {
    if (visionMode == GL_VMONO || visionMode == GL_VMULTI) glColor3fv(_hands[i].color);
    else glColor3fv(GL_GREY_COLOR);
    for (int j=0; j<_hands[i].fingRef.count(); j++)
    {
      Vector position = _hands[i].fingRef[j].tipPosition();
      position.y += LEAP_XZ_PLANE;
      mat_glRotateXZ(_angleXZ, &position);    // rotation imposed by the camera
      glTranslatef(position.x, position.y, position.z);
      glutSolidSphere(1.5, 20, 20);
      glTranslatef(-position.x, -position.y, -position.z);
    }
  }
  glEnable(GL_LIGHTING);
  return;
}

// draw palm position
void Vew_GlutWindow::vew_drawPalmPosition()
{
  glDisable(GL_LIGHTING);
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  for (unsigned int i=0; i<_hands.size(); i++)
  {
    // load texture
    int numFingers = _hands[i].handRef.fingers().extended().count();
    glBindTexture(GL_TEXTURE_2D, (handTexture.texHandImage[numFingers]));
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, handTexture.wHandImage, handTexture.hHandImage, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, handTexture.handImage[numFingers]);
    gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, handTexture.wHandImage, handTexture.hHandImage,
                 GL_RGBA, GL_UNSIGNED_BYTE, handTexture.handImage[numFingers]);

    // compute the pitch and roll angle
    float pitch  = _hands[i].handRef.palmNormal().pitch() * RAD_TO_DEG + 90.0;
    float yaw    = -_hands[i].handRef.direction().yaw() * RAD_TO_DEG;
    float roll   = _hands[i].handRef.palmNormal().roll() * RAD_TO_DEG;

    Vector position = _hands[i].handRef.palmPosition();
    position.y += LEAP_XZ_PLANE;
    mat_glRotateXZ(_angleXZ, &position);    // rotation imposed by the camera

    glTranslatef(position.x, position.y, position.z);
    glRotatef(-_angleXZ * RAD_TO_DEG, 0.0f, 1.0f, 0.0f);
    glRotatef(pitch, 1.0f, 0.0f, 0.0f);
    glRotatef(roll, 0.0f, 0.0f, 1.0f);
    glRotatef(yaw, 0.0f, 1.0f, 0.0f);

    glBegin(GL_QUADS);
      glNormal3f(0.0, 1.0, 0.0);
      if (_hands[i].left_right == RIGHTH) glTexCoord2f(0.0, 1.0);
      else  glTexCoord2f(1.0, 1.0);
      glVertex3f(MINXSP, 0.0f, MINZSP);    // Top Left
      if (_hands[i].left_right == RIGHTH) glTexCoord2f(1.0, 1.0);
      else glTexCoord2f(0.0, 1.0);
      glVertex3f(MAXXSP, 0.0f, MINZSP);    // Top Right
      if (_hands[i].left_right == RIGHTH) glTexCoord2f(1.0, 0.0);
      else glTexCoord2f(0.0, 0.0);
      glVertex3f(MAXXSP, 0.0f, MAXZSP);    // Bottom Right
      if (_hands[i].left_right == RIGHTH) glTexCoord2f(0.0, 0.0);
      else glTexCoord2f(1.0, 0.0);
      glVertex3f(MINXSP, 0.0f, MAXZSP);    // Bottom Left
    glEnd();

    glRotatef(-yaw, 0.0f, 1.0f, 0.0f);
    glRotatef(-roll, 0.0f, 0.0f, 1.0f);
    glRotatef(-pitch, 1.0f, 0.0f, 0.0f);
    glRotatef(_angleXZ * RAD_TO_DEG, 0.0f, 1.0f, 0.0f);
    glTranslatef(-position.x, -position.y, -position.z);
  }

  glDisable(GL_BLEND);
  glDisable(GL_TEXTURE_2D);
  glEnable(GL_LIGHTING);
  return;
}

// draw finger position
void Vew_GlutWindow::vew_drawFingPosition(int visionMode)
{
  glDisable(GL_LIGHTING);
  // fingers tip reference
  glLineWidth(0.1);
  for (unsigned int i=0; i<_hands.size(); i++)
  {
    for (int j=0; j<_hands[i].fingRef.count(); j++)
    {
      if (visionMode == GL_VMONO || visionMode == GL_VMULTI) glColor3fv(_hands[i].color);
      else glColor3fv(GL_GREY_COLOR);
      Vector position = _hands[i].fingRef[j].tipPosition();
      position.y += LEAP_XZ_PLANE;
      mat_glRotateXZ(_angleXZ, &position);  // rotation imposed by the camera

      // change size and color if the finger is inside the radius capture of the mesh
      float radius = 2.0;
      for (unsigned int k=0; k<_mesh.size(); k++)
      {
        if (position.distanceTo(_mesh[k].center) <= (_mesh[k].rCapture) &&
            !_mesh[k].captured && _hands[i].meshRef == NULLREF)
        {
          radius = 5.0;
          glColor4fv(_mesh[k].color);
        }
      }

      // vertical
      glBegin(GL_LINES);
        glVertex3f(position.x, position.y, position.z);
        glVertex3f(position.x, 0.0, position.z);
      glEnd();
      // on XZ plane
      glTranslatef(position.x, 0.0, position.z);
      glRotatef(90.0, 1.0f, 0.0f, 0.0f);
      glutSolidCylinder(radius, 0.2, 10, 10);
      glRotatef(-90.0, 1.0f, 0.0f, 0.0f);
      glTranslatef(-position.x, 0.0, -position.z);

      // horizontal
      glBegin(GL_LINES);
        glVertex3f(position.x, position.y, position.z);
        glVertex3f(position.x, position.y, -150.0);
      glEnd();
      // on XY plane
      glTranslatef(position.x, position.y, -150.0);
      glutSolidCylinder(radius, 0.2, 10, 10);
      glTranslatef(-position.x, -position.y, 150.0);
    }
  }
  glEnable(GL_LIGHTING);
  return;
}

// draw mesh palm distance
void Vew_GlutWindow::vew_drawPalmMeshDistance(int visionMode, int mRef, int hId)
{
  glDisable(GL_LIGHTING);

  // search hand
  Vector hPos;
  int handRef;
  for (unsigned int j=0; j<_hands.size(); j++)
  {
    if (_hands[j].handRef.id() == hId)
    {
      handRef = j;
      hPos = _hands[j].handRef.palmPosition();
      hPos.y += LEAP_XZ_PLANE;
      mat_glRotateXZ(_angleXZ, &hPos);  // rotation imposed by the camera
    }
  }

  // mesh palm distance
  if (visionMode == GL_VMONO || visionMode == GL_VMULTI) glColor3fv(_hands[handRef].color);
  else glColor3fv(GL_GREY_COLOR);
  glLineWidth((200.0/hPos.distanceTo(_mesh[mRef].center))+1);
  glBegin(GL_LINES);
    glVertex3f(hPos.x, hPos.y, hPos.z);
    glVertex3f(_mesh[mRef].center.x, _mesh[mRef].center.y, _mesh[mRef].center.z);
  glEnd();
  glEnable(GL_LIGHTING);
  return;
}

// draw selection sphere
void Vew_GlutWindow::vew_drawSelectionShpere(void)
{
  glColor4fv(GL_WHITE_COLORA);
  _camera.vew_setSphereMaterial();
  // if there is only one hand that shows one finger draw the sphere
  if (_hands.size() == 1 && _hands[0].fingRef.count() == LEAP_OFOPEN)
  {
    Vector position = _hands[0].fingRef[0].tipPosition();
    position.y += LEAP_XZ_PLANE;
    mat_glRotateXZ(_angleXZ, &position);  // rotation imposed by the camera
    glTranslatef(position.x, position.y, position.z);
    glutSolidSphere(_hands[0].selecRadius, 80, 80);
    glTranslatef(-position.x, -position.y, -position.z);
  }
  return;
}

// draw selection plane
void Vew_GlutWindow::vew_drawSelectionCutPlane(void)
{
  glColor4fv(GL_WHITE_COLORA);
  _camera.vew_setSphereMaterial();
  // if there is only one hand that shows one finger draw the sphere
  if (_hands.size() == 1)
  {
    // compute the pitch and roll angle
    float pitch  = _hands[0].handRef.palmNormal().pitch() * RAD_TO_DEG + 90.0;
    float yaw    = -_hands[0].handRef.direction().yaw() * RAD_TO_DEG;
    float roll   = _hands[0].handRef.palmNormal().roll() * RAD_TO_DEG;

    Vector position = _hands[0].handRef.palmPosition();
    position.y += LEAP_XZ_PLANE;
    mat_glRotateXZ(_angleXZ, &position);    // rotation imposed by the camera

    glTranslatef(position.x, position.y, position.z);
    glRotatef(-_angleXZ * RAD_TO_DEG, 0.0f, 1.0f, 0.0f);
    glRotatef(pitch, 1.0f, 0.0f, 0.0f);
    glRotatef(roll, 0.0f, 0.0f, 1.0f);
    glRotatef(yaw, 0.0f, 1.0f, 0.0f);

// TODO ricostruire la matrice:
//-opengl calcola la matrice
//-la catturo con la get
//-modifico i vertici a partire dagli originali (posso evitare la reset a 1261)
//-disegno i modificati
//-saranno quelli utilizzati per selezionare i vertici

float Mprova[16];
glGetFloatv(GL_MODELVIEW_MATRIX, Mprova);

//    glBegin(GL_QUADS);
//      glNormal3f(0.0, 1.0, 0.0);
//      glVertex3fv(_topLeftSelecPlane);      // Top Left
//      glVertex3fv(_topRightSelecPlane);     // Top Right
//      glVertex3fv(_bottomRightSelecPlane);  // Bottom Right
//      glVertex3fv(_bottomLeftSelecPlane);   // Bottom Left
//    glEnd();

    glRotatef(-yaw, 0.0f, 1.0f, 0.0f);
    glRotatef(-roll, 0.0f, 0.0f, 1.0f);
    glRotatef(-pitch, 1.0f, 0.0f, 0.0f);
    glRotatef(_angleXZ * RAD_TO_DEG, 0.0f, 1.0f, 0.0f);

float tl[3], tr[3], br[3], bl[3];
tl[0] = _topLeftSelecPlane[0];  tl[1] = _topLeftSelecPlane[1];  tl[2] = _topLeftSelecPlane[2];
tr[0] = _topRightSelecPlane[0];  tr[1] = _topRightSelecPlane[1];  tr[2] = _topRightSelecPlane[2];
br[0] = _bottomRightSelecPlane[0];  br[1] = _bottomRightSelecPlane[1];  br[2] = _bottomRightSelecPlane[2];
bl[0] = _bottomLeftSelecPlane[0];  bl[1] = _bottomLeftSelecPlane[1];  bl[2] = _bottomLeftSelecPlane[2];
mat_vectorMatrixMultiply(tl, Mprova, tl);
mat_vectorMatrixMultiply(tr, Mprova, tr);
mat_vectorMatrixMultiply(br, Mprova, br);
mat_vectorMatrixMultiply(bl, Mprova, bl);
glBegin(GL_QUADS);
  glNormal3f(0.0, 1.0, 0.0);
  glVertex3fv(tl);  // Top Left
  glVertex3fv(tr);  // Top Right
  glVertex3fv(br);  // Bottom Right
  glVertex3fv(bl);  // Bottom Left
glEnd();


    glTranslatef(-position.x, -position.y, -position.z);
  }
  return;
}


// Display callback function
// The function glutMainLoopEvent() is available with freeglut
// and maybe it could be used to manage each new frames
// provided by leap.
// http://freeglut.sourceforge.net/docs/api.php#EventProcessing
void vew_display(void)
{
  if (_window.width != glutGet(GLUT_WINDOW_WIDTH) ||
      _window.height != glutGet(GLUT_WINDOW_HEIGHT) )
  {
    // window width and height
    _window.width  = glutGet(GLUT_WINDOW_WIDTH);
    _window.height = glutGet(GLUT_WINDOW_HEIGHT);
    // set the frustum view for anaglyph vision
    _camera.vew_setFrustumLeft();
    _camera.vew_setFrustumRight();
  }

  // check movements from the Leap
  _listener.onFrame(_controller);

  // update the visualization
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glClearColor(0.0, 0.0, 0.0, 1.0);
  switch (_window.vmode)
  {
    case GL_VMONO:              // mono vision
    {
      glViewport(0, 0, _window.width, _window.height);
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      GLfloat aspectRatio = ((float)_window.width / (float)_window.height);
      gluPerspective(_camera.fovAngle, aspectRatio, _camera.zNear, _camera.zFar);
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      gluLookAt(_camera.lookAt[0], _camera.lookAt[1], _camera.lookAt[2],
                _camera.lookTo[0], _camera.lookTo[1], _camera.lookTo[2],
                _camera.lookUp[0], _camera.lookUp[1], _camera.lookUp[2]);
      glPushMatrix();

      // draw mesh disk projection
      for (unsigned int i=0; i<_mesh.size(); i++)
        if (_window.encyli) _mesh[i].mod_drawRefCylinder(_window.vmode);
      // background grid
      if (_window.engrid) _window.vew_grid(_window.vmode);
      // system's axis
      if (_window.enaxes) _window.vew_axes(_window.vmode);
      // camera vs object
      if (_window.mode != camera)
      {
        _window.vew_drawFingersSphere(_window.vmode);
        if (_window.fingPos) _window.vew_drawFingPosition(_window.vmode);
      }
      _window.vew_drawString2D(APPMODES[_window.mode], 8, 10, GL_WHITE_COLOR);
      // mesh
      for (unsigned int i=0; i<_mesh.size(); i++)
      {
        if (_window.enSolid)
        {
          _camera.vew_setLight();
          _mesh[i].mod_drawMeshSolid();
        }
        else _mesh[i].mod_drawMesh();
        if (_mesh[i].captured) _window.vew_drawPalmMeshDistance(_window.vmode, i, _mesh[i].handId);
        if (_window.enSphere)  _mesh[i].mod_drawCatchingSphere();
      }
      // draw elements for the selection operation
      if (_window.mode == edit)
      {
        if (_window.selecSphere)
          _window.vew_drawSelectionShpere();
        else if(_window.selecPlane)
          _window.vew_drawSelectionCutPlane();
      }
      // draw selected vertices
      for (unsigned int i=0; i<_mesh.size(); i++)
        _mesh[i].mod_drawSelectedVertices(_window.vmode);
      // draw palm reference
      if (_window.mode != camera)
        if (_window.palmPlan) _window.vew_drawPalmPosition();

      // menu
      if (_window.helpMenu)
        _window.vew_drawMenu(_window.vmode);

      glPopMatrix();
      break;
    }

    case GL_VMULTI:            // multi viewports
    {

      // front (0), side (1) and top (2) view
      for (int i=0; i<3; i++)
      {
        int localWidth, localHeight = 0;
        localWidth  = (_window.width/3)-10;
        localHeight = _window.height/3;
        int left_right = localHeight;
        int bottom_top = ((float)left_right / ((float)localWidth/(float)localHeight));
        if (i == 0) glViewport(5, localHeight*2+5, localWidth, localHeight-5);
        if (i == 1) glViewport(localWidth+15, localHeight*2+5, localWidth, localHeight-5);
        if (i == 2) glViewport(localWidth*2+25, localHeight*2+5, localWidth, localHeight-5);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(-left_right, left_right, -bottom_top, bottom_top, _camera.zNear, _camera.zFar);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        if (i == 0) gluLookAt(_camera.lookAtF[0], _camera.lookAtF[1], _camera.lookAtF[2],
                              _camera.lookToF[0], _camera.lookToF[1], _camera.lookToF[2],
                              _camera.lookUpF[0], _camera.lookUpF[1], _camera.lookUpF[2]);
        if (i == 1) gluLookAt(_camera.lookAtS[0], _camera.lookAtS[1], _camera.lookAtS[2],
                              _camera.lookToS[0], _camera.lookToS[1], _camera.lookToS[2],
                              _camera.lookUpS[0], _camera.lookUpS[1], _camera.lookUpS[2]);
        if (i == 2) gluLookAt(_camera.lookAtT[0], _camera.lookAtT[1], _camera.lookAtT[2],
                              _camera.lookToT[0], _camera.lookToT[1], _camera.lookToT[2],
                              _camera.lookUpT[0], _camera.lookUpT[1], _camera.lookUpT[2]);
        glPushMatrix();

        // background grid
        _window.vew_grid(_window.vmode);
        // system's axis
        _window.vew_axes(_window.vmode);
        // object
        if (_window.mode != camera)
        {
          _window.vew_drawFingersSphere(_window.vmode);
          if (_window.fingPos) _window.vew_drawFingPosition(_window.vmode);
        }
        if (i == 0) _window.vew_drawString2D("Front", 8, 46, GL_WHITE_COLOR);
        if (i == 1) _window.vew_drawString2D("Side", 8, 46, GL_WHITE_COLOR);
        if (i == 2) _window.vew_drawString2D("Top", 8, 46, GL_WHITE_COLOR);
        // mesh
        for (unsigned int i=0; i<_mesh.size(); i++)
        {
          _camera.vew_setLight();
          _mesh[i].mod_drawMeshSolid();
          if (_mesh[i].captured) _window.vew_drawPalmMeshDistance(_window.vmode, i, _mesh[i].handId);
        }
        // draw elements for the selection operation
        if (_window.mode == edit)
        {
          if (_window.selecSphere)
            _window.vew_drawSelectionShpere();
          else if(_window.selecPlane)
            _window.vew_drawSelectionCutPlane();
        }
        // draw selected vertices
        for (unsigned int i=0; i<_mesh.size(); i++)
          _mesh[i].mod_drawSelectedVertices(_window.vmode);
        // draw palm reference
        if (_window.mode != camera)
          if (_window.palmPlan) _window.vew_drawPalmPosition();

        glPopMatrix();
      }

      // 3D view side
      int localWidth, localHeight = 0;
      localWidth  = _window.width/2;
      localHeight = _window.height*2/3;
      glViewport(localWidth/2, 5, localWidth, localHeight);
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      GLfloat aspectRatio = ((float)localWidth / (float)(localHeight-5));
      gluPerspective(_camera.fovAngle, aspectRatio, _camera.zNear, _camera.zFar);
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      gluLookAt(_camera.lookAt[0], _camera.lookAt[1], _camera.lookAt[2],
                _camera.lookTo[0], _camera.lookTo[1], _camera.lookTo[2],
                _camera.lookUp[0], _camera.lookUp[1], _camera.lookUp[2]);
      glPushMatrix();

      // draw mesh disk projection
      for (unsigned int i=0; i<_mesh.size(); i++)
        if (_window.encyli) _mesh[i].mod_drawRefCylinder(_window.vmode);
      // background grid
      if (_window.engrid) _window.vew_grid(_window.vmode);
      // system's axis
      if (_window.enaxes) _window.vew_axes(_window.vmode);
      // camera vs object
      if (_window.mode != camera)
      {
        _window.vew_drawFingersSphere(_window.vmode);
        if (_window.fingPos) _window.vew_drawFingPosition(_window.vmode);
      }
      _window.vew_drawString2D(APPMODES[_window.mode], 8, 10, GL_WHITE_COLOR);
      // mesh
      for (unsigned int i=0; i<_mesh.size(); i++)
      {
        if (_window.enSolid)
        {
          _camera.vew_setLight();
          _mesh[i].mod_drawMeshSolid();
        }
        else _mesh[i].mod_drawMesh();
        if (_mesh[i].captured) _window.vew_drawPalmMeshDistance(_window.vmode, i, _mesh[i].handId);
        if (_window.enSphere)  _mesh[i].mod_drawCatchingSphere();
      }
      // draw elements for the selection operation
      if (_window.mode == edit)
      {
        if (_window.selecSphere)
          _window.vew_drawSelectionShpere();
        else if(_window.selecPlane)
          _window.vew_drawSelectionCutPlane();
      }
      // draw selected vertices
      for (unsigned int i=0; i<_mesh.size(); i++)
        _mesh[i].mod_drawSelectedVertices(_window.vmode);
      // draw palm reference
      if (_window.mode != camera)
        if (_window.palmPlan) _window.vew_drawPalmPosition();
      // menu
      if (_window.helpMenu)
        _window.vew_drawMenu(_window.vmode);

      glPopMatrix();

      break;
    }

    case (GL_VANAGLYPH):          // anaglyph
    {

      glViewport(0, 0, _window.width, _window.height);
      for (int i=0; i<2; i++)
      {
        if (i == 0) // left eye
          _camera.vew_setLeftEye();
        else        // right eye
          _camera.vew_setRightEye();
        glPushMatrix();

        // draw mesh disk projection
        for (unsigned int i=0; i<_mesh.size(); i++)
          if (_window.encyli) _mesh[i].mod_drawRefCylinder(_window.vmode);
        // background grid
        if (_window.engrid) _window.vew_grid(_window.vmode);
        // system's axis
        if (_window.enaxes) _window.vew_axes(_window.vmode);
        // camera vs object
        if (_window.mode != camera)
        {
          _window.vew_drawFingersSphere(_window.vmode);
          if (_window.fingPos) _window.vew_drawFingPosition(_window.vmode);
        }
        _window.vew_drawString2D(APPMODES[_window.mode], 8, 10, GL_GREY_COLOR);
        // mesh
        for (unsigned int i=0; i<_mesh.size(); i++)
        {
          _mesh[i].mod_drawMeshSolid();
          if (_mesh[i].captured) _window.vew_drawPalmMeshDistance(_window.vmode, i, _mesh[i].handId);
          if (_window.enSphere)  _mesh[i].mod_drawCatchingSphere();
        }
        // draw elements for the selection operation
        if (_window.mode == edit)
        {
          if (_window.selecSphere)
            _window.vew_drawSelectionShpere();
          else if(_window.selecPlane)
            _window.vew_drawSelectionCutPlane();
        }
        // draw selected vertices
        for (unsigned int i=0; i<_mesh.size(); i++)
          _mesh[i].mod_drawSelectedVertices(_window.vmode);
        // draw palm reference
        if (_window.mode != camera)
          if (_window.palmPlan) _window.vew_drawPalmPosition();
        // menu
        if (_window.helpMenu)
          _window.vew_drawMenu(_window.vmode);

        glPopMatrix();
      }
      glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
      break;
    }

    default:
    {
      cout << "Error: Unable to provide this vision modality!!" << endl;
      break;
    }
  }

  glutSwapBuffers();
  return;
}

// Keyboard callback function
GLvoid vew_keyboard(unsigned char key, GLint x, GLint y)
{
  switch (key)
  {
    case '1':
    {
      _window.mode = camera;
      break;
    }
    case '2':
    {
      _window.mode = object;
      break;
    }
    case '3':
    {
      _window.mode = edit;
      break;
    }
    case '4':
    {
      _window.mode = cut;
      break;
    }
    case '5':
    {
      _window.mode = boolean;
      break;
    }
    case '6':
    {
      _window.mode = sculpture;
      break;
    }
    case 'A':
    {
      if (_window.mode == camera)
      {
        switch (_window.anaglyphType)
        {
          case red_blue:
            _window.anaglyphType = red_cyan;
            break;
          case red_cyan:
            _window.anaglyphType = red_blue;
            break;
          default:
            break;
        }
      }
      break;
    }
    case 'a':
    {
      _window.enaxes = (_window.enaxes) ? false : true;
      break;
    }
    case 'b':
    {
      if (_window.mode == boolean)
      {
        // This code is similar for selection by TAP GESTURE
        // if there is only one hand that shows one finger
        if (_hands.size() == 1 && _hands[0].fingRef.count() == LEAP_OFOPEN)
        {
          // check if the hand has already a mesh
          if (_hands[0].meshRef >= 0)
          {
            // assegna la mesh alla struttura per la booleana
        	m.push_back(_hands[0].meshRef);
            _mesh[_hands[0].meshRef].captured = false;
            _mesh[_hands[0].meshRef].handId   = NULLREF;
            _hands[0].meshRef  = NULLREF;
          }
        }
      }
      break;
    }
    case 'c':
    {
      if (_window.mode == object || _window.mode == edit || _window.mode == boolean)
      {
        // This code is similar for selection by TAP GESTURE
        // if there is only one hand that shows one finger
        if (_hands.size() == 1 && _hands[0].fingRef.count() == LEAP_OFOPEN)
        {
          bool putDown = false;
          // check if the hand has already a mesh: put down it
          if (_hands[0].meshRef >= 0)
          {
            _mesh[_hands[0].meshRef].captured = false;
            _mesh[_hands[0].meshRef].handId   = NULLREF;
            _hands[0].meshRef  = NULLREF;
            putDown = true;
          }

          // check the nearest free mesh to pick up
          if (!putDown)
          {
            int pickupMeshRef = NULLREF;
            Vector tapPos = _hands[0].fingRef[0].tipPosition();
            tapPos.y += LEAP_XZ_PLANE;
            mat_glRotateXZ(_angleXZ, &tapPos);  // rotation imposed by the camera
            float tmpMinDistance = numeric_limits<float>::max();
            for (unsigned int j=0; j<_mesh.size(); j++)
            {
              // inside the sphere of the mesh && the mesh is free && the distance is the nearest
              if ( _mesh[j].center.distanceTo(tapPos) <= _mesh[j].rCapture &&
                  !_mesh[j].captured && _mesh[j].center.distanceTo(tapPos) < tmpMinDistance)
              {
                pickupMeshRef = j;
                tmpMinDistance = _mesh[j].center.distanceTo(tapPos);
              }
            }
            if (pickupMeshRef >= 0)
            {
              _mesh[pickupMeshRef].captured = true;
              _mesh[pickupMeshRef].handId   = _hands[0].handRef.id();
              _hands[0].meshRef  = pickupMeshRef;
            }
          }
        }
      }
      break;
    }
    case 'd':
    {
      if (_window.mode == edit)
      {
        gen_deselectAllVertices();
      }
      break;
    }
    case 'e':
    {
      if (_window.mode == camera)
      {
        _camera.eyeDelta -= 0.05;
        _camera.vew_createEyes();
        _camera.vew_setFrustumLeft();
        _camera.vew_setFrustumRight();
      }
      break;
    }
    case 'E':
    {
      if (_window.mode == camera)
      {
        _camera.eyeDelta += 0.05;
        _camera.vew_createEyes();
        _camera.vew_setFrustumLeft();
        _camera.vew_setFrustumRight();
      }
      break;
    }
    case 'f':
    {
      if (_window.mode != camera)
      {
        if (!_window.fingPos)
        {
          if  (_window.palmPlan)
            _window.palmPlan = false;
          else
            _window.fingPos = true;
        }
        else if (!_window.palmPlan)
          _window.palmPlan = true;
        else
          _window.fingPos = false;
      }
      break;
    }
    case 'g':
    {
      _window.engrid = (_window.engrid) ? false : true;
      break;
    }
    case 'h':
    {
      _window.helpMenu = (_window.helpMenu) ? false : true;
      break;
    }
    case 'i':
    {
      gen_invertSelectPoint();
      break;
    }
    case 'm':
    {
      if (_window.mode != camera)
      {
        if (!_window.encyli)
        {
          if  (_window.enSphere)
            _window.enSphere = false;
          else
            _window.encyli = true;
        }
        else if (!_window.enSphere)
          _window.enSphere = true;
        else
          _window.encyli = false;
      }
      break;
    }
    case 'p':
    {
      if (_window.mode == camera)
      {
        _camera.folDista -= 1.0;
        _camera.vew_setFrustumLeft();
        _camera.vew_setFrustumRight();
      }
      else if (_window.mode == edit)
      {
        _window.selecSphere = false;
        _window.selecPlane  = (_window.selecPlane) ? false : true;
        // reset coordinates of the selection plan
        _topLeftSelecPlane[0] = MINXSP;      _topLeftSelecPlane[1] = 0.0f;      _topLeftSelecPlane[2] = MINZSP;
        _topRightSelecPlane[0] = MAXXSP;     _topRightSelecPlane[1] = 0.0f;     _topRightSelecPlane[2] = MINZSP;
        _bottomRightSelecPlane[0] = MAXXSP;  _bottomRightSelecPlane[1] = 0.0f;  _bottomRightSelecPlane[2] = MAXZSP;
        _bottomLeftSelecPlane[0] = MINXSP;   _bottomLeftSelecPlane[1] = 0.0f;   _bottomLeftSelecPlane[2] = MAXZSP;
      }
      break;
    }
    case 'P':
    {
      if (_window.mode == camera)
      {
        _camera.folDista += 1.0;
        _camera.vew_setFrustumLeft();
        _camera.vew_setFrustumRight();
      }
      break;
    }
    case 'R':
    {
      if (_window.mode == camera)
      {
        _axeXupdate.x = 1.0;  _axeXupdate.y = 0.0;  _axeXupdate.z = 0.0;
        _axeYupdate.x = 0.0;  _axeYupdate.y = 1.0;  _axeYupdate.z = 0.0;
        _axeZupdate.x = 0.0;  _axeZupdate.y = 0.0;  _axeZupdate.z = 1.0;
        _camera.vew_resetCamera();
        _camera.vew_setFrustumLeft();
        _camera.vew_setFrustumRight();
      }
      else if (_window.mode == boolean)
      {
        //TODO esegui le operazioni booleane
        if(m.size()==2){
        	result = gen_applyBooleanOps(&(_mesh[m[0]]),&(_mesh[m[1]]));
        	if(result.size()!=0){
        		printf("aggiungo al vettore \n");
        		result[0]->mod_copyMesh(&tempMesh); // TODO verificare errore _mesh.push_back(result[0]);
                tempMesh.mod_setCenterAndRadius();  // TODO queste due operazioni andrebbero spostate altrove
                tempMesh.color = GL_COLOR_V[_mesh.size()%GL_NUM_COLORA];
        		_mesh.push_back(tempMesh);
        		printf("aggiungo al vettore \n");
        		result[1]->mod_copyMesh(&tempMesh);
                tempMesh.mod_setCenterAndRadius();
                tempMesh.color = GL_COLOR_V[_mesh.size()%GL_NUM_COLORA];
        		_mesh.push_back(tempMesh);
        		printf("aggiungo al vettore \n");
        		result[2]->mod_copyMesh(&tempMesh);
                tempMesh.mod_setCenterAndRadius();
                tempMesh.color = GL_COLOR_V[_mesh.size()%GL_NUM_COLORA];
        		_mesh.push_back(tempMesh);
        		printf("aggiungo al vettore \n");
        		result[3]->mod_copyMesh(&tempMesh);
                tempMesh.mod_setCenterAndRadius();
                tempMesh.color = GL_COLOR_V[_mesh.size()%GL_NUM_COLORA];
        		_mesh.push_back(tempMesh);
        	}
        }
      }
      break;
    }
    case 'S':
    {
      if (_window.mode == object)
      {
        for (unsigned int i=0; i<_mesh.size(); i++)
          _mesh[i].mod_save();
      }
      break;
    }
    case 's':
    {
      if (_window.mode == edit)
      {
        _window.selecPlane  = false;
        _window.selecSphere = (_window.selecSphere) ? false : true;
      }
      break;
    }
    case 'v':
    {
      _window.enSolid = (_window.enSolid) ? false : true;
      break;
    }
    case 'V':
    {
      _window.vmode = (_window.vmode + 1) % GL_NVISION;
      break;
    }
    case 'z':
    {
      float oldZoom = _camera.zooming;
      _camera.vew_zoomIn(1.0);
      _camera.vew_applyZoom(true, oldZoom);
      if (_window.vmode == GL_VANAGLYPH) _camera.vew_createEyes();
      break;
    }
    case 'Z':
    {
      float oldZoom = _camera.zooming;
      _camera.vew_zoomOut(1.0);
      _camera.vew_applyZoom(true, oldZoom);
      if (_window.vmode == GL_VANAGLYPH) _camera.vew_createEyes();
      break;
    }
    case '>':
    {
      if (_window.mode == edit)
      {
        if (_window.selecPlane)
          gen_selectPointByPlane(SELECT_POINT);
        else if (_window.selecSphere)
          gen_selectPointBySphere(SELECT_POINT);
      }
      break;
    }
    case '<':
    {
      if (_window.mode == edit)
      {
        if (_window.selecPlane)
          gen_selectPointByPlane(SELECT_POINT);
        else if (_window.selecSphere)
          gen_selectPointBySphere(DESELECT_POINT);
      }
      break;
    }
    case KEY_ESCAPE:
    {
      if (_window.mode  == object)
      {
        _controller.removeListener(_listener);
        exit(0);
      }
      else
        _window.mode = object;
      break;
    }
    default:
    {
      cout << "Error: Unable to manage the pressed key!!" << endl;
      break;
    }
  }

  glutPostRedisplay();
  return;
}


// Camera stuff
// Constructor
Vew_GlutCamera::Vew_GlutCamera()
{
  // global view
  lookAt[0] = GL_LOOK_AT_X;  lookAt[1] = GL_LOOK_AT_Y;  lookAt[2] = GL_LOOK_AT_Z;
  lookTo[0] = GL_LOOK_TO_X;  lookTo[1] = GL_LOOK_TO_Y;  lookTo[2] = GL_LOOK_TO_Z;
  lookUp[0] = GL_LOOK_UP_X;  lookUp[1] = GL_LOOK_UP_Y;  lookUp[2] = GL_LOOK_UP_Z;

  // front view
  lookAtF[0] = GL_LOOK_AT_FX;  lookAtF[1] = GL_LOOK_AT_FY;  lookAtF[2] = GL_LOOK_AT_FZ;
  lookToF[0] = GL_LOOK_TO_FX;  lookToF[1] = GL_LOOK_TO_FY;  lookToF[2] = GL_LOOK_TO_FZ;
  lookUpF[0] = GL_LOOK_UP_X;   lookUpF[1] = GL_LOOK_UP_Y;   lookUpF[2] = GL_LOOK_UP_Z;

  // side view
  lookAtS[0] = GL_LOOK_AT_SX;  lookAtS[1] = GL_LOOK_AT_SY;  lookAtS[2] = GL_LOOK_AT_SZ;
  lookToS[0] = GL_LOOK_TO_SX;  lookToS[1] = GL_LOOK_TO_SY;  lookToS[2] = GL_LOOK_TO_SZ;
  lookUpS[0] = GL_LOOK_UP_X;   lookUpS[1] = GL_LOOK_UP_Y;   lookUpS[2] = GL_LOOK_UP_Z;

  // top view
  lookAtT[0] = GL_LOOK_AT_TX;  lookAtT[1] = GL_LOOK_AT_TY;  lookAtT[2] = GL_LOOK_AT_TZ;
  lookToT[0] = GL_LOOK_TO_TX;  lookToT[1] = GL_LOOK_TO_TY;  lookToT[2] = GL_LOOK_TO_TZ;
  lookUpT[0] = GL_LOOK_UP_TX;  lookUpT[1] = GL_LOOK_UP_TY;  lookUpT[2] = GL_LOOK_UP_TZ;

  // solid mesh material
  mAmbient[0]  = 0.2;  mAmbient[1]  = 0.2;  mAmbient[2]  = 0.2;  mAmbient[3]  = 1.0;
  mDiffuse[0]  = 0.5;  mDiffuse[1]  = 0.5;  mDiffuse[2]  = 0.5;  mDiffuse[3]  = 1.0;
  mSpecular[0] = 0.0;  mSpecular[1] = 0.0;  mSpecular[2] = 0.1;  mSpecular[3] = 1.0;
  // blue material
  mBambient[0]  = 0.0;  mBambient[1]  = 0.0;  mBambient[2]  = 0.3;  mBambient[3]  = 1.0;
  mBdiffuse[0]  = 0.0;  mBdiffuse[1]  = 0.0;  mBdiffuse[2]  = 0.7;  mBdiffuse[3]  = 1.0;
  mBspecular[0] = 0.0;  mBspecular[1] = 0.0;  mBspecular[2] = 0.1;  mBspecular[3] = 1.0;
  // cyan material
  mCambient[0]  = 0.0;  mCambient[1]  = 0.2;   mCambient[2]  = 0.2;   mCambient[3]  = 1.0;
  mCdiffuse[0]  = 0.0;  mCdiffuse[1]  = 0.6;   mCdiffuse[2]  = 0.6;   mCdiffuse[3]  = 1.0;
  mCspecular[0] = 0.0;  mCspecular[1] = 0.05;  mCspecular[2] = 0.05;  mCspecular[3] = 1.0;
  // red material
  mRambient[0]  = 0.2;  mRambient[1]  = 0.0;  mRambient[2]  = 0.0;  mRambient[3]  = 1.0;
  mRdiffuse[0]  = 0.6;  mRdiffuse[1]  = 0.0;  mRdiffuse[2]  = 0.0;  mRdiffuse[3]  = 1.0;
  mRspecular[0] = 0.05; mRspecular[1] = 0.0;  mRspecular[2] = 0.0;  mRspecular[3] = 1.0;
  mShininess[0] = 20.0;
  // material selection sphere
  mWambient[0]  = 0.55;  mWambient[1]  = 0.55;  mWambient[2]  = 0.55;  mWambient[3]  = 1.0;
  mWdiffuse[0]  = 0.35;  mWdiffuse[1]  = 0.35;  mWdiffuse[2]  = 0.35;  mWdiffuse[3]  = 1.0;
  mWspecular[0] = 0.1;   mWspecular[1] = 0.1;   mWspecular[2] = 0.1;   mWspecular[3] = 1.0;

  fovAngle = GL_FOV;
  eyeDelta = GL_EYESEP;
  folDista = GL_FOL;
  zNear    = 1.0f;
  zFar     = 2000.0f;
  zooming  = 0.0;

  // create eyes for anaglyph vision
  vew_createEyes();
  return;
}

// Destructor
Vew_GlutCamera::~Vew_GlutCamera() {}

// Reset camera zoom and position
void Vew_GlutCamera::vew_resetCamera(void)
{
  lookAt[0] = GL_LOOK_AT_X;  lookAt[1] = GL_LOOK_AT_Y;  lookAt[2] = GL_LOOK_AT_Z;
  lookTo[0] = GL_LOOK_TO_X;  lookTo[1] = GL_LOOK_TO_Y;  lookTo[2] = GL_LOOK_TO_Z;
  zooming   = 0.0;
  eyeDelta  = GL_EYESEP;
  folDista  = GL_FOL;
  vew_createEyes();
  return;
}

// Zoom in/out
void Vew_GlutCamera::vew_zoomIn(float fact)
{
  zooming += GL_ZOOM_INC*fact;
}
void Vew_GlutCamera::vew_zoomOut(float fact)
{
  zooming -= GL_ZOOM_INC*fact;
}
void Vew_GlutCamera::vew_applyZoom(bool zoomChange, float oldZoom)
{
  // move in (0, 0, 0)
  Mat_Vector tmpLookAt((lookAt[0] - lookTo[0]), (lookAt[1] - lookTo[1]), (lookAt[2] - lookTo[2]));
  Mat_Vector tmpLookTo((lookTo[0] - lookAt[0]), (lookTo[1] - lookAt[1]), (lookTo[2] - lookAt[2]));
  Mat_Vector tmp(lookAt[0], lookAt[1], lookAt[2]);
  // update polar coordinates with zoom
  tmpLookAt.mat_cartesian2sphere();
  if (zoomChange) tmpLookAt.rho += oldZoom;
  tmpLookAt.rho -= zooming;
  tmpLookAt.mat_sphere2cartesian();
  tmpLookTo.mat_cartesian2sphere();
  if (zoomChange) tmpLookTo.rho -= oldZoom;
  tmpLookTo.rho += zooming;
  tmpLookTo.mat_sphere2cartesian();
  // update camera position
  lookAt[0] = tmpLookAt.x + lookTo[0];
  lookAt[1] = tmpLookAt.y + lookTo[1];
  lookAt[2] = tmpLookAt.z + lookTo[2];
  lookTo[0] = tmpLookTo.x + tmp.x;
  lookTo[1] = tmpLookTo.y + tmp.y;
  lookTo[2] = tmpLookTo.z + tmp.z;
  return;
}

// Create left and right eye
void Vew_GlutCamera::vew_createEyes(void)
{
  // normalized cross product
  Mat_Vector crossProduct;
  crossProduct.x = (lookTo[1]-lookAt[1]) * lookUp[2] - (lookTo[2]-lookAt[2]) * lookUp[1];
  crossProduct.y = (lookTo[2]-lookAt[2]) * lookUp[0] - (lookTo[0]-lookAt[0]) * lookUp[2];
  crossProduct.z = (lookTo[0]-lookAt[0]) * lookUp[1] - (lookTo[1]-lookAt[1]) * lookUp[0];
  crossProduct = crossProduct.normalized();
  // separate eyes
  crossProduct.mat_cartesian2sphere();
  crossProduct.rho *= (eyeDelta/2.0);
  crossProduct.mat_sphere2cartesian();
  // set left eye
  lookAtLeft[0] = lookAt[0] - crossProduct[0];
  lookAtLeft[1] = lookAt[1] - crossProduct[1];
  lookAtLeft[2] = lookAt[2] - crossProduct[2];
  lookToLeft[0] = lookTo[0] - crossProduct[0];
  lookToLeft[1] = lookTo[1] - crossProduct[1];
  lookToLeft[2] = lookTo[2] - crossProduct[2];
  // set right eye
  lookAtRight[0] = lookAt[0] + crossProduct[0];
  lookAtRight[1] = lookAt[1] + crossProduct[1];
  lookAtRight[2] = lookAt[2] + crossProduct[2];
  lookToRight[0] = lookTo[0] + crossProduct[0];
  lookToRight[1] = lookTo[1] + crossProduct[1];
  lookToRight[2] = lookTo[2] + crossProduct[2];
  return;
}

// Set the light and the matierial for the selection sphere
void Vew_GlutCamera::vew_setSphereMaterial(void)
{

  glDisable(GL_LIGHT0);  // switch off red, blue and cyan light
  glDisable(GL_LIGHT1);
  glDisable(GL_LIGHT3);
  glEnable(GL_LIGHT2);

  glMaterialfv(GL_FRONT, GL_AMBIENT,   mWambient);
  glMaterialfv(GL_FRONT, GL_DIFFUSE,   mWdiffuse);
  glMaterialfv(GL_FRONT, GL_SPECULAR,  mWspecular);
  glMaterialfv(GL_FRONT, GL_SHININESS, mShininess);

  return;
}

// Set the light and the matierial for solid mesh
void Vew_GlutCamera::vew_setLight(void)
{

  glDisable(GL_LIGHT0);  // switch off red, blue and cyan light
  glDisable(GL_LIGHT1);
  glDisable(GL_LIGHT3);
  glEnable(GL_LIGHT2);

  glMaterialfv(GL_FRONT, GL_AMBIENT,   mAmbient);
  glMaterialfv(GL_FRONT, GL_DIFFUSE,   mDiffuse);
  glMaterialfv(GL_FRONT, GL_SPECULAR,  mSpecular);
  glMaterialfv(GL_FRONT, GL_SHININESS, mShininess);

  return;
}

// Set the light and the matierial for the eyes left and ...
void Vew_GlutCamera::vew_setRightLight(void)
{

  glDisable(GL_LIGHT0);  // switch off blue light
  glDisable(GL_LIGHT1);  // switch off red light
  glDisable(GL_LIGHT2);  // switch off general light
  glDisable(GL_LIGHT3);  // switch off cyan light

  switch (_window.anaglyphType)
  {
    case red_blue:
      // blue
      glEnable(GL_LIGHT0);  // switch on blue light
      glMaterialfv(GL_FRONT, GL_AMBIENT,   mBambient);
      glMaterialfv(GL_FRONT, GL_DIFFUSE,   mBdiffuse);
      glMaterialfv(GL_FRONT, GL_SPECULAR,  mBspecular);
      glMaterialfv(GL_FRONT, GL_SHININESS, mShininess);
      break;
    case red_cyan:
      // cyan
      glEnable(GL_LIGHT3);  // switch on cyan light
      glMaterialfv(GL_FRONT, GL_AMBIENT,   mCambient);
      glMaterialfv(GL_FRONT, GL_DIFFUSE,   mCdiffuse);
      glMaterialfv(GL_FRONT, GL_SPECULAR,  mCspecular);
      glMaterialfv(GL_FRONT, GL_SHININESS, mShininess);
      break;
    default:
      break;
  }

  return;
}
// ... right
void Vew_GlutCamera::vew_setLeftLight(void)
{
  glDisable(GL_LIGHT0);  // switch off blue light
  glEnable(GL_LIGHT1);   // switch on red light
  glDisable(GL_LIGHT2);  // switch off general light
  glDisable(GL_LIGHT3);  // switch off cyan light

  glMaterialfv(GL_FRONT, GL_AMBIENT,   mRambient);
  glMaterialfv(GL_FRONT, GL_DIFFUSE,   mRdiffuse);
  glMaterialfv(GL_FRONT, GL_SPECULAR,  mRspecular);
  glMaterialfv(GL_FRONT, GL_SHININESS, mShininess);

  return;
}

// Set the frustum view for the eyes: left and ...
// http://www.animesh.me/2011/05/rendering-3d-anaglyph-in-opengl.html
void Vew_GlutCamera::vew_setFrustumLeft(void)
{
  // set the frustum limit
  float tempA, tempB, tempC;
  tempA    = ((float)_window.width / (float)_window.height) * tan(DEG_TO_RAD*fovAngle/2.0) * folDista;
  tempB    = tempA - eyeDelta/2.0;
  tempC    = tempA + eyeDelta/2.0;
  leftLF   = -tempB * zNear/folDista;
  rightLF  = tempC * zNear/folDista;
  topLF    =  zNear * tan(DEG_TO_RAD*fovAngle/2.0);
  bottomLF = -topLF;
  return;
}
// ... right
void Vew_GlutCamera::vew_setFrustumRight(void)
{
  // set the frustum limit
  float tempA, tempB, tempC;
  tempA    = ((float)_window.width / (float)_window.height) * tan(DEG_TO_RAD*fovAngle/2.0) * folDista;
  tempB    = tempA - eyeDelta/2.0;
  tempC    = tempA + eyeDelta/2.0;
  leftRF   = -tempC * zNear/folDista;
  rightRF  = tempB * zNear/folDista;
  topRF    =  zNear * tan(DEG_TO_RAD*fovAngle/2.0);
  bottomRF = -topRF;
  return;
}

// Set the view for the eyes: left and ...
void Vew_GlutCamera::vew_setLeftEye(void)
{
  // set light and material
  vew_setLeftLight();
  glClear(GL_DEPTH_BUFFER_BIT);
  // define frustum
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(leftLF, rightLF, bottomLF, topLF, zNear, zFar);
  // define left view direction
  glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(lookAtLeft[0], lookAtLeft[1], lookAtLeft[2],
            lookToLeft[0], lookToLeft[1], lookToLeft[2],
            lookUp[0],     lookUp[1],     lookUp[2]);
  return;
}
// ... right
void Vew_GlutCamera::vew_setRightEye(void)
{
  // set light and material
  vew_setRightLight();
  glClear(GL_DEPTH_BUFFER_BIT);
  // define frustum
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(leftRF, rightRF, bottomRF, topRF, zNear, zFar);
  // define right view direction
  switch (_window.anaglyphType)
  {
    case red_blue:
      glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE);  // blue
      break;
    case red_cyan:
      glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);   // cyan
      break;
    default:
      break;
  }
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(lookAtRight[0], lookAtRight[1], lookAtRight[2],
            lookToRight[0], lookToRight[1], lookToRight[2],
            lookUp[0],      lookUp[1],      lookUp[2]);
  return;
}


// Image stuff
// open image
Vew_ImageRec *vew_ImageOpen(const char *fileName)
{
  union
  {
    GLint testWord;
    GLbyte testByte[4];
  } endianTest;

  Vew_ImageRec *image;
  GLint swapFlag;
  GLint x;

  endianTest.testWord = 1;
  if (endianTest.testByte[0] == 1)
  {
    swapFlag = 1;
  } else
  {
    swapFlag = 0;
  }

  image = (Vew_ImageRec *)malloc(sizeof(Vew_ImageRec));
  if (image == NULL)
  {
    fprintf(stderr, "Out of memory!\n");
    exit(1);
  }
  if ((image->file = fopen(fileName, "rb")) == NULL)
  {
    perror(fileName);
    exit(1);
  }

  fread(image, 1, 12, image->file);

  if (swapFlag)
  {
    vew_ConvertShort(&image->imagic, 6);
  }

  image->tmp = (GLubyte *)malloc(image->xsize*512);
  image->tmpR = (GLubyte *)malloc(image->xsize*512);
  image->tmpG = (GLubyte *)malloc(image->xsize*512);
  image->tmpB = (GLubyte *)malloc(image->xsize*512);
  if (image->tmp == NULL || image->tmpR == NULL || image->tmpG == NULL || image->tmpB == NULL)
  {
    fprintf(stderr, "Out of memory!\n");
    exit(1);
  }

  if ((image->type & 0xFF00) == 0x0100)
  {
    x = image->ysize * image->zsize * sizeof(unsigned);
    image->rowStart = (unsigned *)malloc(x);
    image->rowSize = (GLint *)malloc(x);
    if (image->rowStart == NULL || image->rowSize == NULL)
    {
      fprintf(stderr, "Out of memory!\n");
      exit(1);
    }
    image->rleEnd = 512 + (2 * x);
    fseek(image->file, 512, SEEK_SET);
    fread(image->rowStart, 1, x, image->file);
    fread(image->rowSize, 1, x, image->file);
    if (swapFlag)
    {
      vew_ConvertLong(image->rowStart, x/(GLint)sizeof(unsigned));
      vew_ConvertLong((unsigned *)image->rowSize, x/(GLint)sizeof(GLint));
    }
  }
  else
  {
    image->rowStart = NULL;
    image->rowSize = NULL;
  }
  return image;
}

// load rgb image
unsigned* vew_rgbLoader(const char *name, GLint *width, GLint *height, GLint *components, GLbyte modAlpha)
{

  unsigned *base, *lptr;
  GLubyte *rbuf, *gbuf, *bbuf, *abuf;
  Vew_ImageRec *image;
  GLint y;

  image = vew_ImageOpen(name);

  if (!image) return NULL;

  (*width) = image->xsize;
  (*height) = image->ysize;
  (*components) = image->zsize;

  base = (unsigned *)malloc(image->xsize*image->ysize*sizeof(unsigned));
  rbuf = (GLubyte *)malloc(image->xsize*sizeof(GLubyte));
  gbuf = (GLubyte *)malloc(image->xsize*sizeof(GLubyte));
  bbuf = (GLubyte *)malloc(image->xsize*sizeof(GLubyte));
  abuf = (GLubyte *)malloc(image->xsize*sizeof(GLubyte));

  if (!base || !rbuf || !gbuf || !bbuf) return NULL;

  lptr = base;
  for(y=0; y<image->ysize; y++)
  {
    if (image->zsize>=4)
    {
      vew_ImageGetRow(image, rbuf, y, 0);
      vew_ImageGetRow(image, gbuf, y, 1);
      vew_ImageGetRow(image, bbuf, y, 2);
      vew_ImageGetRow(image, abuf, y, 3);
      vew_rgbatorgba(rbuf, gbuf, bbuf, abuf, (GLubyte *)lptr, image->xsize);
      lptr += image->xsize;
    }
    else if (image->zsize==3)
    {
      vew_ImageGetRow(image, rbuf, y, 0);
      vew_ImageGetRow(image, gbuf, y, 1);
      vew_ImageGetRow(image, bbuf, y, 2);
      vew_rgbtorgba(rbuf, gbuf, bbuf, (GLubyte *)lptr, image->xsize, modAlpha);
      lptr += image->xsize;
    }
    else if (image->zsize==2)
    {
      vew_ImageGetRow(image, rbuf, y, 0);
      vew_ImageGetRow(image, abuf, y, 1);
      vew_latorgba(rbuf, abuf, (GLubyte *)lptr, image->xsize);
      lptr += image->xsize;
    }
    else
    {
      vew_ImageGetRow(image, rbuf, y, 0);
      vew_bwtorgba(rbuf, (GLubyte *)lptr, image->xsize);
      lptr += image->xsize;
    }
  }
  vew_ImageClose(image);
  free(rbuf);
  free(gbuf);
  free(bbuf);
  free(abuf);

  return (unsigned *)base;
}

// close image
void vew_ImageClose(Vew_ImageRec *image)
{
  fclose(image->file);
  free(image->tmp);
  free(image->tmpR);
  free(image->tmpG);
  free(image->tmpB);
  free(image->rowSize);
  free(image->rowStart);
  free(image);
  return;
}

// load a row
void vew_ImageGetRow(Vew_ImageRec *image, GLubyte *buf, GLint y, GLint z)
{

  GLubyte *iPtr, *oPtr, pixel;
  GLint count;

  if ((image->type & 0xFF00) == 0x0100)
  {
    fseek(image->file, (long)image->rowStart[y+z*image->ysize], SEEK_SET);
    fread(image->tmp, 1,(GLuint)image->rowSize[y+z*image->ysize], image->file);

    iPtr = image->tmp;
    oPtr = buf;
    for (;;)
    {
      pixel = *iPtr++;
      count = (GLint)(pixel & 0x7F);
      if (!count) { return; }
      if (pixel & 0x80)
      {
        while (count--)
        {
          *oPtr++ = *iPtr++;
        }
      }
      else
      {
        pixel = *iPtr++;
        while (count--)
        {
          *oPtr++ = pixel;
        }
      }
    }
  }
  else
  {
    fseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize), SEEK_SET);
    fread(buf, 1, image->xsize, image->file);
  }
  return;
}

// conversion stuff
void vew_bwtorgba(GLubyte *b, GLubyte *l, GLint n)
{
  while(n--)
  {
    l[0] = *b;
    l[1] = *b;
    l[2] = *b;
    l[3] = 0xff;
    l += 4; b++;
  }
  return;
}

void vew_latorgba(GLubyte *b, GLubyte *a, GLubyte *l, GLint n)
{
  while(n--)
  {
    l[0] = *b;
    l[1] = *b;
    l[2] = *b;
    l[3] = *a;
    l += 4; b++; a++;
  }
  return;
}

void vew_rgbtorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, GLint n, GLbyte modAlpha)
{
  while(n--)
  {
    l[0] = r[0];
    l[1] = g[0];
    l[2] = b[0];
    l[3] = 0xff;
    if (modAlpha && l[0] == 255 && l[1] == 255 && l[2] == 255) l[3] = 0x00;
    else l[3] = 0xff;
    l += 4; r++; g++; b++;
  }
  return;
}

void vew_rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a, GLubyte *l, GLint n)
{
  while(n--)
  {
    l[0] = r[0];
    l[1] = g[0];
    l[2] = b[0];
    l[3] = a[0];
    l += 4; r++; g++; b++; a++;
  }
  return;
}

void vew_ConvertShort(GLushort *array, long length)
{
  unsigned b1, b2;
  GLubyte *ptr;
  ptr = (GLubyte *)array;

  while (length--)
  {
    b1 = *ptr++;
    b2 = *ptr++;
    *array++ = (b1 << 8) | (b2);
  }
  return;
}

void vew_ConvertLong(unsigned *array, long length)
{
  unsigned b1, b2, b3, b4;
  GLubyte *ptr;
  ptr = (GLubyte *)array;

  while (length--)
  {
    b1 = *ptr++;
    b2 = *ptr++;
    b3 = *ptr++;
    b4 = *ptr++;
    *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
  }
  return;
}
